/*
    基于future和packaged_task实现一个线程池
    线程池设计思想: 用户传入需要执行的函数以及参数，由线程池中的工作线程执行函数来完成任务
    线程池的实现:
    成员:
        1. 任务队列: 存放用户传入的任务
        2. 互斥锁 & 条件变量: 实现多线程间的同步与互斥，从而保证任务队列的安全
        3. 固定数量的线程：工作线程，从任务队列中获取任务并执行
        4. 结束标志: 用于停止线程池的工作
    操作:
        1. 线程池入口函数: 
        2. 入队任务: 将用户传入的函数以及参数构建成任务对象，放入到任务队列中
        3. 停止任务: 停止线程池的工作 
*/

#include <iostream>
#include <thread>
#include <future>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <vector>
#include <functional>

class ThreadPool {
public:
    using Task = std::function<void()>;

public:
    ThreadPool(int thread_num = std::thread::hardware_concurrency()) : _stop(false)
    {
        for(int i = 0; i < thread_num; ++i)
        {
            _threads.emplace_back(&ThreadPool::entry, this);
        }
    }

    ~ThreadPool()
    {
        stop();
    }

    // 停止任务
    void stop()
    {
        if(_stop == true) return;
        _stop = true;
        _cond_var.notify_all();
        for(auto& t : _threads)
        {
            if(t.joinable())
                t.join();
        }
    }

    // 入队任务
    // 返回值说明: 需要给用户返回一个能够获取结果的对象，显然future很适合，但是future是一个模板，用户传入的函数返回值是不确定的，所以future无法直接确定
    //            可以使用auto作为push返回值，但是需要使用decltype()来进行推导返回值类型，其中fn(args...)可以理解为执行函数获取返回值
    template<typename Fn, typename... Args>
    auto push(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))>
    {
        // 1. 将传入的函数打包成一个packaged_task对象
        using return_type = decltype(fn(args...)); // 推导返回值类型
        std::function<return_type()> tmp_fn = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...); // 将函数和其参数进行绑定，方便后续任务的执行
        std::shared_ptr<std::packaged_task<return_type()>> task = std::make_shared<std::packaged_task<return_type()>>(std::move(tmp_fn));
        std::future<return_type> future = task->get_future();
        // 2. 构造lambda表达式，在表达式内执行任务
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _tasks.emplace_back([task](){(*task)();}); // 将任务打包成lambda表达式入任务队列
            _cond_var.notify_one();
        }
        return future;
    }

private:
    // 线程的入口函数
    void entry()
    {
        while(!_stop)
        {
            std::vector<Task> tmp_tasks;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _cond_var.wait(lock, [this](){return _stop || !_tasks.empty();});
                _tasks.swap(tmp_tasks);
            }
            for(auto& task : tmp_tasks)
                task();
        }
    }

private:
    std::atomic<bool> _stop; // 停止标志
    std::vector<std::thread> _threads; // 工作线程
    std::vector<Task> _tasks; // 任务队列
    std::mutex _mutex;
    std::condition_variable _cond_var;
};

int add(int num1, int num2)
{
    std::cout << num1 << " + " << num2 << " = " << num1 + num2 << std::endl;
    return num1 + num2;
}

int main()
{
    ThreadPool pool;
    for(int i = 1; i <= 10; i++)
    {
        std::future<int> future = pool.push(add, 10, i);
        std::cout << "result: " << future.get() << std::endl;
    }
    return 0;
}